#include "MetaballsRender.hpp"
#include "Camera.hpp"

#include "Compute.hpp"

using rtrt::MetaballsRender;
using Eigen::Vector4f;

MetaballsRender::MetaballsRender() : _renderProgram( NULL ), _renderKernel( NULL ), _metaballs( NULL )
{
  
  _positions [0] = Vector4f ( 0.0F, 0.0F, 0.0F, 0.50F );
  _positions [1] = Vector4f ( 0.0F, 0.0F, 0.0F, 0.75F );
  _positions [2] = Vector4f ( 0.0F, 0.0F, 0.0F, 1.00F );
  _positions [3] = Vector4f ( 0.0F, 0.0F, 0.0F, 1.50F );
  _positions [4] = Vector4f ( 0.0F, 0.0F, 0.0F, 1.50F );
  _positions [5] = Vector4f ( 0.0F, 0.0F, 0.0F, 1.00F );
  _positions [6] = Vector4f ( 0.0F, 0.0F, 0.0F, 0.75F );
  _positions [7] = Vector4f ( 0.0F, 0.0F, 0.0F, 1.50F );
  
  setDrawMode( BaseRender::drawCL );
  
}

MetaballsRender::~MetaballsRender()
{
  
  if( _renderProgram != NULL )
    cltCheckError( clReleaseProgram( _renderProgram ));
  
  if( _renderKernel != NULL )
    cltCheckError( clReleaseKernel( _renderKernel ));
  
  if( _metaballs != NULL )
    cltCheckError( clReleaseMemObject( _metaballs ));
  
}

void MetaballsRender::setDrawMode( DrawMode mode )
{
  BaseRender::setDrawMode( drawCL );
}

void MetaballsRender::draw( Camera *camera )
{
  
  static float currtime = 0.0F;

  /* Move metaballs */

  currtime += 0.008F;

  {
     _positions [0] (0) = sinf ( currtime );
     _positions [0] (1) = cosf ( currtime );
     _positions [0] (2) = 0.0F;

     _positions [1] (0) = 2.0F * sinf ( currtime );
     _positions [1] (1) = 0.0F;
     _positions [1] (2) = 2.0F * cosf ( currtime );

     _positions [2] (0) = 0.0F;
     _positions [2] (1) = 2.0F * sinf ( currtime );
     _positions [2] (2) = 2.0F * cosf ( currtime );

     _positions [3] (0) = 3.0F * sinf ( currtime );
     _positions [3] (1) = 3.0F * sinf ( currtime );
     _positions [3] (2) = 3.0F * cosf ( currtime );

     _positions [4] (0) = 3.0F * cosf ( currtime );
     _positions [4] (1) = 3.0F * sinf ( currtime );
     _positions [4] (2) = 3.0F * cosf ( currtime );

     _positions [5] (0) = 2.0F * sinf ( currtime ) * cosf ( currtime );
     _positions [5] (1) = 2.0F * sinf ( currtime ) * cosf ( currtime );
     _positions [5] (2) = 2.0F * cosf ( currtime );

     _positions [6] (0) = 3.0F * sinf ( currtime ) * cosf ( currtime );
     _positions [6] (1) = 4.0F * sinf ( currtime ) * cosf ( currtime );
     _positions [6] (2) = 3.0F * cosf ( currtime );

     _positions [7] (0) = 4.0F * cosf ( currtime ) * cosf ( currtime );
     _positions [7] (1) = 3.0F * sinf ( currtime );
     _positions [7] (2) = 4.0F * cosf ( currtime ) * cosf ( currtime );
  }
  
  return BaseRender::draw( camera, currtime );
  
}


void MetaballsRender::initialize( int width, int height, bool useOpenGL, StereoMode mode )
{
  
  BaseRender::initialize( width, height, useOpenGL, mode );
  
  VECTOR< cl_device_id > devices = cltGetDevices ( context() );
  
  _renderProgram = cltLoadProgram ( context(), ( std::string( METABALLS_OPENCL_PATH ) + "/Render.cl" ).c_str() );
  cltBuildProgram ( _renderProgram, devices );
  
  _renderKernel = cltCreateKernel ( _renderProgram, "Main" );
  
  _metaballs = cltCreateBuffer < cl_float4 > ( context(), CL_MEM_READ_ONLY, COUNT );
 
  setStereoBase( 0.1F           );
//   setStereoMode( softwareStereo );
  
}


void MetaballsRender::doDrawCL( cl_mem outImage, Camera *camera )
{
  
  cltSetArgument < cl_mem > ( _renderKernel, 0, &outImage );
    
  cl_float4 view     = cltFloat4 ( camera->view     ( )        );
  cl_float4 up       = cltFloat4 ( camera->up       ( )        );
  cl_float4 side     = cltFloat4 ( camera->right     ( )        );
  cl_float4 position = cltFloat4 ( camera->position ( )        );
  cl_float2 scale    = cltFloat2 ( camera->aspect() * camera->size ( ),
				   camera->size () );

  cltSetArgument < cl_float4 > ( _renderKernel, 1, &view     );
  cltSetArgument < cl_float4 > ( _renderKernel, 2, &up       );
  cltSetArgument < cl_float4 > ( _renderKernel, 3, &side     );
  cltSetArgument < cl_float4 > ( _renderKernel, 4, &position );
  cltSetArgument < cl_float2 > ( _renderKernel, 5, &scale    );

  int width1  = width ();
  int height1 = height();
  
  cltSetArgument < int > ( _renderKernel, 6, &width1  );
  cltSetArgument < int > ( _renderKernel, 7, &height1 );

  cltCheckError ( clEnqueueWriteBuffer ( commandQueue(),
                                         _metaballs,
                                         CL_TRUE,
                                         0,
                                         COUNT * sizeof ( cl_float4 ),
                                         _positions,
                                         0,
                                         NULL,
                                         NULL ) );

  cltSetArgument < cl_mem > ( _renderKernel, 8, &_metaballs );
  
  if( width1 % 8 != 0 )
    width1 += 8 - width1 % 8;
  if( height1 % 8 != 0 )
    height1 += 8 - height1 % 8;
  
  cltRunKernel2D( commandQueue(), _renderKernel, width1, height1, 8, 8 );

  cltFinish ( commandQueue() ); 
  
}